import * as vscode from 'vscode';
import * as path from 'path';
import * as os from 'os';
import * as fs from 'fs';
import DevstarAPIHandler from './devstar-api';
import { showErrorNotification } from './utils';
const {
  generateKeyPairSync,
  createHash
} = require('node:crypto');
const sshpk = require('sshpk');

export default class User {
  private context: vscode.ExtensionContext;
  private username: string | undefined;
  private userToken: string | undefined;
  private usernameKey: string = 'devstarUsername'
  private userTokenKey: string = 'devstarUserToken'
  private localUserPrivateKeyPath: string = ''
  private devstarHostname: string;

  constructor(context: vscode.ExtensionContext) {
    this.context = context;
    this.username = this.context.globalState.get(this.usernameKey);
    this.userToken = this.context.globalState.get(this.userTokenKey);

    // 提取devstar domain的主域名，用于本地ssh key的命名
    let devstarDomainFromConfig: string | undefined;
    let devstarDomainURL: string;
    devstarDomainFromConfig = vscode.workspace.getConfiguration('devstar').get('devstarDomain')
    // 如果没有配置devstar domain，则默认domain为https://devstar.cn
    devstarDomainURL = (devstarDomainFromConfig === undefined || devstarDomainFromConfig === "") ? 'https://devstar.cn' : devstarDomainFromConfig;
    let parsedUrl = new URL(devstarDomainURL);
    this.devstarHostname = parsedUrl.hostname.replace(/\./g, '_'); //提取hostname，并用下划线替换.
  }

  public async login(token: string, username: string) {
    const devstarAPIHandler = new DevstarAPIHandler()

    try {
      const res = await devstarAPIHandler.verifyToken(token, username)
      if (!res) {
        throw new Error('Token verification failed')
      }

      // token与用户名验证通过
      // 插件登录：存储token与用户名
      this.setUserTokenToLocal(token)
      this.setUsernameToLocal(username)

      // 检查本地是否有用户所属公钥，没有则创建
      if (!this.existUserPublicKey()) {
        await this.createUserSSHKey()

        // 上传公钥
        const uploadResult = await devstarAPIHandler.uploadUserPublicKey(this)
        if (uploadResult !== 'ok') {
          throw new Error('Upload user public key failed')
        }
      }
      
      vscode.window.showInformationMessage(vscode.l10n.t('User login successfully!'))
      return 'ok'
    } catch (error) {
      console.error(error)
      await showErrorNotification('用户登录失败！')
      return 'login failed'
    }
  }

  public logout() {
    this.setUserTokenToLocal("")
    this.setUsernameToLocal("")
    vscode.window.showInformationMessage(vscode.l10n.t("User has logged out!"))
  }

  public isLogged() {
    var existUsername = false;
    var existUserToken = false;
    if (this.username != undefined && this.username != '') {
      existUsername = true;
    }
    if (this.userToken != undefined && this.userToken != '') {
      existUserToken = true;
    }

    if (existUsername && existUserToken) {
      return true;
    } else {
      return false;
    }
  }

  public getUsernameFromLocal(): string | undefined {
    return this.username;
  }

  public getUserTokenFromLocal(): string | undefined {
    return this.userToken;
  }

  public setUsernameToLocal(username: string) {
    this.context.globalState.update(this.usernameKey, username);
    this.username = username;
    if (username !== "") {
      console.log('Username has been stored.')
    } else {
      console.log('Username has been cleaned up.')
    }
  }

  public setUserTokenToLocal(userToken: string) {
    this.context.globalState.update(this.userTokenKey, userToken)
    this.userToken = userToken
    if (userToken !== "") {
      console.log('Token has been stored.');
    } else {
      console.log('Token has been cleaned up.')
    }
  }

  public updateLocalUserPrivateKeyPath(localUserPrivateKeyPath: string) {
    this.context.globalState.update('localUserPrivateKeyPath', localUserPrivateKeyPath)
  }

  public getLocalUserPrivateKeyPath(): undefined | string {
    return this.context.globalState.get('localUserPrivateKeyPath')
  }

  public getUserPrivateKeyPath(): string {
    if (!this.isLogged) {
      return '';
    }

    return path.join(os.homedir(), '.ssh', `id_rsa_${this.username}_${this.devstarHostname}`)
  }

  public getUserPublicKeyPath(): string {
    if (!this.isLogged) {
      return '';
    }

    return path.join(os.homedir(), '.ssh', `id_rsa_${this.username}_${this.devstarHostname}.pub`)
  }

  public existUserPublicKey(): boolean {
    const userPublicKeyPath = this.getUserPublicKeyPath();
    return fs.existsSync(userPublicKeyPath)
  }

  public existUserPrivateKey(): boolean {
    const userPrivateKeyPath = this.getUserPrivateKeyPath();
    return fs.existsSync(userPrivateKeyPath)
  }

  public getUserPublicKey(): string {
    const userPublicKeyPath = this.getUserPublicKeyPath();
    const userPublicKey = fs.readFileSync(userPublicKeyPath, 'utf-8');
    // remove `\r` `\n`
    const trimmedDefaultPublicKey = userPublicKey.replace(/[\r\n]/g, "");
    return trimmedDefaultPublicKey;
  }

  public getUserPrivateKey(): string {
    const userPrivateKey = this.getUserPrivateKeyPath();
    return fs.readFileSync(userPrivateKey, 'utf-8');
  }

  public async createUserSSHKey() {
    if (this.existUserPublicKey() && this.existUserPrivateKey()) {
      // if both public and private key exists, stop
      return;
    }

    const {
      publicKey,
      privateKey,
    } = await generateKeyPairSync('rsa', {
      modulusLength: 4096,
      publicKeyEncoding: {
        type: 'pkcs1',
        format: 'pem',
      },
      privateKeyEncoding: {
        type: 'pkcs1',
        format: 'pem',
      },
    });
    const publicKeyFingerprint = sshpk.parseKey(publicKey, 'pem').toString('ssh');
    const publicKeyStr = publicKeyFingerprint; // public key is public key fingerprint
    const privateKeyStr = privateKey;

    try {
      // 确保公/私钥目录存在
      const publicKeyDir = path.dirname(this.getUserPublicKeyPath())
      if (!fs.existsSync(publicKeyDir)) {
        console.log(`Directory ${publicKeyDir} does not exist, creating it...`);
        // 公钥与私钥的目录一样，所以只用创建一次
        fs.mkdirSync(publicKeyDir, {recursive: true})
      }

      fs.writeFileSync(this.getUserPublicKeyPath(), publicKeyStr);
      fs.writeFileSync(this.getUserPrivateKeyPath(), privateKeyStr);
      // limit the permission of private key to prevent that the private key not works
      fs.chmodSync(this.getUserPrivateKeyPath(), 0o600)
      console.log(`User's ssh key has been created.`)
      // 更新local user private key path
      this.updateLocalUserPrivateKeyPath(this.getUserPrivateKeyPath())
      console.log(`Update local user private key path.`)
    } catch (error) {
      console.error(`Failed to write public/private key into the user(${this.username}) ssh public/key file: `, error);
    }
  }
}